package com.kasax.httpclient.core;

import com.kasax.httpclient.beans.Request;
import com.kasax.httpclient.beans.Response;
import com.kasax.httpclient.core.inters.Call;
import com.kasax.httpclient.core.inters.Callback;
import com.kasax.httpclient.core.inters.Interceptor;
import com.kasax.httpclient.interceptors.BridgeInterceptor;
import com.kasax.httpclient.interceptors.CallServerInterceptor;
import com.kasax.httpclient.interceptors.ConnectionInterceptor;
import com.kasax.httpclient.interceptors.RetryAndFlowUpInterceptor;
import com.kasax.httpclient.utils.LogUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class RealCall implements Call {
    private final KOkHttpClient client;
    private final Request request;

    /**
     * 是否执行过
     */
    private boolean executed;

    /**
     * 是否取消
     */
    private boolean canceled;

    public RealCall(KOkHttpClient clientCore, Request request) {
        this.client = clientCore;
        this.request = request;
    }

    public static Call newRealCall(KOkHttpClient clientCore, Request request) {
        return new RealCall(clientCore, request);
    }

    public void cancel() {
        canceled = true;
    }


    @Override
    public Response execute(Request request) {
        LogUtils.i(getClass(), "enqueue");
        client.dispatcher().execute(this);
        try {
            Response response = getResponseWithInterceptorChain();
            return response;
        } catch (Exception e) {
            LogUtils.e(getClass(), e);
        } finally {
            client.dispatcher().finish(this);
        }
        return null;
    }

    @Override
    public void enqueue(Callback callback) {
        LogUtils.i(getClass(), "enqueue");
        synchronized (this) {
            if (executed) {
                // 同一个call同能重复执行
                throw new IllegalStateException("Already Execute");
            }
            executed = true;
        }
        AsyncCall asyncCall = new AsyncCall(this, callback);
        client.dispatcher().enqueue(asyncCall);
    }


    @Override
    public boolean isExecuted() {
        return executed;
    }

    @Override
    public boolean isCanceled() {
        return canceled;
    }

    @Override
    public KOkHttpClient client() {
        return client;
    }

    @Override
    public Request request() {
        return request;
    }

    private Response getResponseWithInterceptorChain() {
        LogUtils.i(getClass(), "getResponseWithInterceptorChain");
        List<Interceptor> interceptors = new ArrayList<>();
        // 添加自定义的
        interceptors.addAll(client.interceptors);
        interceptors.add(new RetryAndFlowUpInterceptor());
        interceptors.add(new BridgeInterceptor());
//        interceptors.add(new CacheInterceptor());
        interceptors.add(new ConnectionInterceptor());
        interceptors.add(new CallServerInterceptor());
        // 让链条运转起来
        try {
            RealInterceptorChain interceptorChain = new RealInterceptorChain(interceptors, 0, this, null);
            Response response = interceptorChain.procced();
            return response;
        } catch (IOException e) {
            LogUtils.e(getClass(), e);
        }
        return null;
    }


    public class AsyncCall implements Runnable {
        private final Callback callback;
        private final RealCall call;

        public AsyncCall(RealCall realCall, Callback callback) {
            this.callback = callback;
            this.call = realCall;
        }

        @Override
        public void run() {
            // 是否已经通知过callBack
            boolean signalledCallback = false;
            try {
                // 调用链条获取执行结果
                Response response = getResponseWithInterceptorChain();
                signalledCallback = true;
                if (canceled) {
                    callback.onFailure(call, new IOException("Canceled"));
                } else {
                    callback.onSuccess(call, response);
                }
            } catch (Exception e) {
                if (signalledCallback) {
                    LogUtils.e(getClass(), e);
                } else {
                    LogUtils.e(getClass(), e);
                    callback.onFailure(call, e);
                }
            } catch (Throwable t) {
                cancel();
                if (!signalledCallback) {
                    IOException ioException = new IOException("canceled due to " + t);
                    callback.onFailure(call, ioException);
                }
            } finally {
                client.dispatcher().finish(this);
            }
        }

        public String host() {
            return request.url().getHost();
        }
    }


}
